package com.ease.dao.util;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;

import com.ease.common.util.Detect;
import com.ease.dao.DataAccessObjectConst;
import com.ease.model.EnumerableModel;
import com.ease.model.Model;
import com.ease.model.NamespaceModel;
import com.ease.model.criteria.CriteriaParameter;

/**
 *
 * @author nathanleewei
 *
 */
public abstract class DataAccessObjectUtil extends Detect {
	// implements DataAccessObjectConst {

	private static final String APPEND_ONEROW = " AND ROWNUM = 1";

	public static String appendOneRowCriteria(String namedStatement) {
		return namedStatement + APPEND_ONEROW;
	}

	public static long idOf(Model model) {
		return NamespaceModel.idOf(model);
	}

	public static String[] nameOf(List<? extends EnumerableModel> enumerableModels) {
		if (notEmpty(enumerableModels)) {
			int len = enumerableModels.size();
			String[] items = new String[len];
			for (int i = 0; i < len; i++) {
				items[i] = enumerableModels.get(i).getName();
			}
			return items;
		}
		return null;
	}

	public static long[] idOf(List<? extends Model> models) {
		if (!notEmpty(models))
			return null;

		long[] ids = new long[models.size()];
		for (int i = 0; i < models.size(); i++) {
			ids[i] = models.get(i).getId();
		}
		return ids;
	}

	public static long[] foreignIdOf(List<? extends Model> models, String propertyName) {
		if (!notEmpty(models))
			return null;

		long[] ids = new long[models.size()];
		for (int i = 0; i < models.size(); i++) {
			try {
				Object o = BeanUtils.getProperty(models.get(i), propertyName);
				if (o instanceof Number) {
					ids[i] = ((Number) o).longValue();
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		return ids;
	}

	public static List<long[]> grouping(long[] values) {
		// if (Detect.notEmpty(values)) {
		// int groupSize =
		// DataAccessObjectConst.MAXIMUM_NUMBER_OF_EXPRESSIONS_USING_IN_CLAUSE;
		// int groupLength = values.length / groupSize + ((values.length %
		// groupSize) > 0 ? 1 : 0);
		// long[][] resultArray = new long[groupLength][];
		// long[] valueArray = null;
		// for (int i = 0; i < groupLength; i++) {
		// int arrayLength = (i < groupLength - 1 || values.length % groupSize
		// == 0) ? groupSize : (values.length % groupSize);
		//
		// valueArray = new long[arrayLength];
		// for (int j = 0; j < arrayLength; j++) {
		// valueArray[j] = values[i * groupSize + j];
		// }
		// resultArray[i] = valueArray;
		// }
		// return resultArray;
		// }
		return grouping(values, DataAccessObjectConst.MAXIMUM_NUMBER_OF_EXPRESSIONS_USING_IN_CLAUSE);
	}

	public static Map<String, Object> safeParameters(Map<String, Object> parameters) {
		return null == parameters ? new HashMap<String, Object>() : parameters;
	}

	public static Map<String, Object> onlyParameter(String onlyParameterKey, Object onlyParameterValue) {
		Map<String, Object> parameters = new HashMap<String, Object>();
		parameters.put(onlyParameterKey, onlyParameterValue);
		return parameters;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static SqlParameterSource safeSqlParameterSource(Object parameter) {
		if (null == parameter)
			return null;

		if (parameter instanceof SqlParameterSource)
			return (SqlParameterSource) parameter;

		if (parameter instanceof Map)
			return new MapSqlParameterSource((Map) parameter);

		return new BeanPropertySqlParameterSource(parameter);
	}

	// 如何创建一个深不可修改的集合？
	// 我常常想收集的字段不可修改之前将其返回的 getter 方法：
	//
	// private List<X> _xs;
	// ....
	// List<X> getXs(){
	// return Collections.unmodifiableList(_xs);
	// }
	// 但我不想做那件事,如果上述 X 本身的简便方法列表：
	//
	// private List<List<Y>> _yLists;
	// .....
	// List<List<Y>> getYLists() {
	// return Collections.unmodifiableList(_yLists);
	// }
	// 在上述问题当然是客户端不能修改列表的列表,虽然它可以添加或删除 Y 对象从嵌入的列表。
	// 我就能用到的最佳用途
	// http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/
	// common/collect/package-summary.html
	// ">ForwardingList 从谷歌。 欢迎评论。
	//
	// private static <T> List<List<T>> unmodifiableList2(final List<List<T>>
	// input) {
	// return Collections.unmodifiableList(new ForwardingList<List<T>>() {
	// protected List<List<T>> delegate() {
	// return Collections.unmodifiableList(input);
	// }
	// public List<T> get(int index) {
	// return Collections.unmodifiableList(delegate().get(index));
	// }
	// });
	// }

	public static int toPrimitive(Integer integer) {
		return (null == integer) ? 0 : integer.intValue();
	}

	public static Map<String, Object> toParameters(CriteriaParameter criteriaParameter) {
		return null == criteriaParameter ? null : CriteriaParameterUtil.toMapParameter(criteriaParameter);
	}

	public static <T extends Model> T getById(long id, List<T> models) {
		if (null != models && id > 0) {
			for (T model : models) {
				if (id == model.getId()) {
					return model;
				}
			}
		}
		return null;
	}

	public static <T extends NamespaceModel> T getByCode(String code, List<T> models) {
		if (null != models && notEmpty(code)) {
			for (T model : models) {
				if (code.equals(model.getCode())) {
					return model;
				}
			}
		}
		return null;
	}

	public static <T extends Model> List<T> getByIds(long[] ids, List<T> models) {
		List<T> returnValue = null;
		String idsString = "," + org.apache.commons.lang.StringUtils.join(org.apache.commons.lang.ArrayUtils.toObject(ids), ",") + ",";
		if (notEmpty(models)) {
			for (T model : models) {
				if (idsString.indexOf("," + String.valueOf(model.getId()) + ",") > -1) {
					if (null == returnValue) {
						returnValue = new LinkedList<T>();
					}
					returnValue.add(model);
				}
			}
		}
		return returnValue;
	}

	public static <T extends Model> String joinName(List<T> models) {
		if (notEmpty(models)) {
			StringBuilder sb = new StringBuilder();
			for (T model : models) {
				sb.append(",").append(model.getName());
			}
			return sb.toString().substring(1);
		}
		return null;
	}

	public static <T extends Model> void sortByNameLengthAndName(List<T> entities) {
		if (Detect.notEmpty(entities)) {
			Collections.sort(entities, new Comparator<Model>() {
				public int compare(Model left, Model right) {
					CompareToBuilder compareToBuilder = new CompareToBuilder();
					if (left.getName() != null && right.getName() != null) {
						compareToBuilder.append(left.getName().length(), right.getName().length());
					}
					compareToBuilder.append(left.getName(), right.getName());
					return compareToBuilder.toComparison();
				}
			});
		}
	}

}
